import sys
import os
import re
import time
import shutil
from datetime import datetime
import schedule

LOG_FILE = "pyBackuper.log"


class ArgsProcessor:
    def __init__(self, argv) -> None:
        self.argc: int = len(argv)
        self.argv: list = argv

        self.target: str = ""
        self.target_type: str = ""  # only receive "file"|"directory"
        self.timer: str | int = ""
        self.timer_mode: str = ""  # only accept "minute"|"datetime"

        # input boot manager
        if self.argc == 1:
            self.cmd_interation()
        elif (
            "-h" in argv
            or "--help" in argv
            or "?" in argv
            or "/h" in argv
            or "/help" in argv
        ):
            myhelp()
            exit(0)
        else:
            self.cmd_args()

    def cmd_interation(self) -> None:
        """命令行交互模式"""
        print("[Init] Command interation mode...")
        print(
            "[Init] Please input the path of the file or directory you want to backup:"
        )
        print(
            '[eg.] /home/user/myfolder/myfile.txt || "C:\\Program Files\\myfile.txt"',
            end="\n>",
        )
        self.target = input()
        print("[Init] Please input the way that program operation:")
        print(
            "[Init] Cycle run the program every x minutes or  at a certain time everyday(hh:mm:ss):"
        )
        print("[eg.] {10 | 24 | 31 | ...} or { 07:00:00 | 23:59:59 | ... }", end="\n>")
        self.timer = input()
        return

    def cmd_args(self) -> None:
        """命令行参数模式"""
        try:
            i_target = self.argv.index("--target")
            if i_target < self.argc - 1:
                self.target = self.argv[i_target + 1]
            else:
                print(
                    "[ERROR] Invalid parameter [--target]: cannot get target file after parameter [--target]"
                )
        except ValueError:
            print("[ERROR] missing parameter [--target]")
            print("[tips] add parameter like [--target TheProcessYouWantToWatch.exe] ")
            exit(1)       
        try:
            i_timer = self.argv.index("--timer")
            if i_timer < self.argc - 1:
                self.timer = self.argv[i_timer + 1]
            else:
                print(
                    "[ERROR] Invalid parameter [--timer]: cannot get target file after parameter [--timer]"
                )
        except ValueError:
            mylog("[WARN] missing parameter [--timer], set to 60 minutes")
            print(
                "[tips] add parameter like [--timer 180] (Check the process every 180 minutes) "
            )
            i_timer = 60
        mylog("[INIT] Command parameters mode...")
        return

    def args_processor(self) -> None:
        if os.path.exists(self.target):
            self.target = os.path.abspath(self.target)
            if os.path.isdir(self.target):
                self.target_type = "directory"
            elif os.path.isfile(self.target):
                self.target_type = "file"
            else:
                print("[ERROR] Unknown target type: not a file or directory")
        else:
            print("[ERROR] Target file or directory is not exist: " + self.target)
            exit(-1)
        if re.match(r"\d+", self.timer):
            self.timer_mode = "minute"
            if re.match(r"\d+:\d+:\d+", str(self.timer)):
                self.timer_mode = "datetime"
            else:
                self.timer = int(self.timer)
        else:
            print("[ERROR] Invalid parameter [--timer]: " + str(self.timer))
            exit(-1)


def backup_operator() -> None:
    if a.target_type == "directory":
        # Edit new backup file|folder name here ↓
        new_target = a.target + "-" + datetime.now().strftime("%y%m%d_%H%M%S")
        shutil.copytree(a.target, new_target)
        mylog("[INFO] Finished backing up directory " + a.target + " to " + new_target)
    elif a.target_type == "file":
        path = os.path.dirname(a.target)
        # parent = os.path.basename(target)
        file_name, file_ext = os.path.splitext(a.target)
        # Edit new backup file|folder name here ↓
        new_filename = (
            file_name + "-" + datetime.now().strftime("%y%m%d_%H%M%S") + file_ext
        )
        new_target = os.path.join(path, new_filename)
        shutil.copy(a.target, new_target)
        mylog("[INFO] Finished backing up file " + a.target + " to " + new_target)
    else:
        mylog("[ERROR] invalid path: not a file or directory")
        exit(-1)


def myhelp() -> None:
    print("Usage:")
    print("[1](running with command parameters): python pyBackuper.py", end=" ")
    print(
        "--target %your_target_file_or_dir_path%  --timer {%xx minutes% | %a certain time%}"
    )
    print("Eg: python pyBackuper.py --target /home/user/myfolder/myfile.txt --timer 10")
    print(
        "    So the program will automatically backup /home/user/myfolder/myfile.txt by every 10 minutes"
    )
    print(
        'Eg: python pyBackuper.py --target "C:\\Program Files\\myfile.txt" --timer 23:59:59'
    )
    print(
        "    So the program will automatically backup C:\\Program Files\\myfile.txt by 23:59:59 everyday"
    )
    print("[2](running with command line interaction): python pyBackuper.py")
    print("Eg: and then, program will ask you some questions to guide your usage")


def mylog(s: str = ""):
    s = f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}]" + s
    print(s)
    with open(LOG_FILE, "a", encoding="utf-8") as f:
        f.write(s + "\n")
        f.close()


if __name__ == "__main__":
    os.system("title pyBackuper")
    print("[ALPHA RELEASE] This is a python-based file/folder scheduled backup tool.")

    a = ArgsProcessor(sys.argv)
    a.args_processor()

    schedule.run_pending()
    if a.timer_mode == "minute":
        schedule.every(a.timer).minutes.do(backup_operator)
        mylog(
            f"[Init] Program will backup [{a.target_type}]{a.target} every {a.timer} minutes"
        )
    elif a.timer_mode == "datetime":
        schedule.every().day.at(a.timer).do(backup_operator)
        mylog(
            f"[Init] Program will backup [{a.target_type}]{a.target} at {a.timer} everyday"
        )
    else:
        mylog("[ERROR] Invalid timer mode")
        exit(-1)
    while True:
        schedule.run_pending()
        time.sleep(1)
